package model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import etm.core.configuration.EtmManager;
import etm.core.monitor.EtmMonitor;
import etm.core.monitor.EtmPoint;

public class IncrementalValidator {
	private static final EtmMonitor etmMonitor = EtmManager.getEtmMonitor();

	SolverEnvironment env;

//	// problem.S size
//	List<Integer>[] servicesRequiredBy;

	// problem.S * problem.L size+1
	// the last position count the number of different locations per service
//	int[][] serviceProcessesPerLocation;

	// problem.S * problem.N size+1
	// the last position count the number of different neighborhoods per service
//	int[][] serviceProcessesPerNeighborhood;
	
	// problem.M * problem.TRE.length
//	long[][] transientResourceUsageOfMachine;

	public IncrementalValidator() {
	}

	public boolean areProcessMachineMoveIndependent(int original_m_index,
			int m_index) {
		// dependency constraint
		int original_n_id = env.getProblem().getNeighborhoodIdForMachineId(
				original_m_index);
		int n_id = env.getProblem().getNeighborhoodIdForMachineId(m_index);
		if (original_n_id != n_id)
			return false;
		else
			return true;
	}

//	private void createServiceProcessesDistribution() {
//		serviceProcessesPerLocation = new int[env.getProblem().S][env.getProblem().L + 1];
//		serviceProcessesPerNeighborhood = new int[env.getProblem().S][env.getProblem().N + 1];
//
//		for (int s_index = 0; s_index < env.getProblem().S; s_index++) {
//			int count_locations = 0, count_neighb = 0;
//			for (Integer p_index : env.getProblem().processesListForServices[s_index]) {
//				int m_index = env.getPotentialSolution().processes[p_index];
//
//				int location = env.getProblem().machines_locations[m_index];
//				if (serviceProcessesPerLocation[s_index][location] == 0)
//					count_locations++;
//				serviceProcessesPerLocation[s_index][location]++;
//
//				int neighborhood = env.getProblem().machines_neighborhoods[m_index];
//				if (serviceProcessesPerNeighborhood[s_index][neighborhood] == 0)
//					count_neighb++;
//				serviceProcessesPerNeighborhood[s_index][neighborhood]++;
//			}
//			serviceProcessesPerLocation[s_index][env.getProblem().L] = count_locations;
//			serviceProcessesPerNeighborhood[s_index][env.getProblem().N] = count_neighb;
//		}
//	}
	
//	private void createServicesRequiredBy() {
//		servicesRequiredBy = new List[env.getProblem().S];
//
//		for (int s = 0; s < env.getProblem().S; s++) {
//			servicesRequiredBy[s] = new ArrayList<Integer>();
//		}
//
//		for (int s = 0; s < env.getProblem().S; s++) {
//			for (Integer service_dep : env.getProblem().service_dependencies[s]) {
//				servicesRequiredBy[service_dep].add(s);
//			}
//		}
//	}
	
//	private void createTransientResourceUsageOfMachine() {
//		Solution current = env.getCurrentSolution();
//		Solution initial = env.getInitialSolution();
//		Problem problem =  env.getProblem();
//		
//		transientResourceUsageOfMachine = new long[env.getProblem().M][env.getProblem().TRE.length];
//		int[] procs = current.processes;
//		int[] init_procs = initial.processes;
//		
//		// Calculate moved out process contribution
//		for (int p=0;p<procs.length;p++) {
//			if (procs[p] != init_procs[p]){
//				for (int j = 0; j < env.getProblem().TRE.length; j++) {
//					transientResourceUsageOfMachine[init_procs[p]][j] += problem.getR(p, problem.TRE[j]);
//				}
//			}
//		}
//	}

	public Solution getCurrentSolution() {
		return env.getPotentialSolution();
	}

	private void init() {
//		createServicesRequiredBy();
//		transientResourceUsageOfMachine = new long[env.getProblem().M][env.getProblem().TRE.length];
	}

	// PA 19112011 Changed to accelerate evaluation

	// before each call it should be ensured that the correct pair of solutions
	// are loaded. Setting the pair of solutions occurs via
	// setSolutionPair(initial_solution, new_solution)
	public boolean isFeasible() {
		EtmPoint point = etmMonitor
				.createPoint("IncrementalValidator:isFeasible");
		boolean flag = true;
		if (!isFeasibleForCapacityConstraints())
			flag = false;
		if (flag && !isFeasibleForConflictConstraints())
			flag = false;
		if (flag && !isFeasibleForSpreadConstraints())
			flag = false;
		if (flag && !isFeasibleForDependencyConstraints())
			flag = false;
		if (flag & !isFeasibleForTransientConstraints())
			flag = false;
		point.collect();
		return flag;
	}

	// PA 24112011 for CV request 3
	public boolean isFeasible(int p_index, int orig_m_index, int m_index) {
		return isProbablyFeasibleNoSchedule(p_index, orig_m_index, m_index)
				&& isFinallyFeasibleNoSchedule(p_index, orig_m_index, m_index);
	}

	private boolean isFeasibleForCapacityConstraints() {
		for (int i = 0; i < env.getProblem().M; i++)
			if (!isMachineCapacityFeasible(i))
				return false;
		return true;
	}

	private boolean isFeasibleForConflictConstraints() {
		for (int s_index = 0; s_index < env.getProblem().S; s_index++) {
			Set<Integer> aSet = new HashSet<Integer>();
			for (Integer p_index : env.getProblem().processesListForServices[s_index]) {
				aSet.add(env.getPotentialSolution().processes[p_index]);
			}
			if (aSet.size() < env.getProblem().processesListForServices[s_index]
					.size()) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [CONFLICT VIOLATION] Processes in the same service should not be scheduled in the same machine\n");
				return false;
			}
		}
		return true;
	}

	private boolean isFeasibleForDependencyConstraints() {
		boolean flag = true;
		Solution potential_solution = env.getPotentialSolution();
		outer: for (int s_indexa = 0; s_indexa < env.getProblem().S; s_indexa++) {
			if (env.getProblem().service_dependencies[s_indexa].isEmpty())
				continue;
			else {
				// System.out.printf("Service %d depends on %s\n", s_indexa,
				// problem.service_dependencies[s_indexa]);
			}
			for (Integer service_dep : env.getProblem().service_dependencies[s_indexa]) {
				Set<Integer> neighborhoods = new HashSet<Integer>();
				// System.out.printf("Service %d processes %s\n", service_dep,
				// problem.processesListForServices[service_dep]);
				for (Integer p_index : env.getProblem().processesListForServices[service_dep]) {
					int m_index = potential_solution.processes[p_index];
					int ni_index = env.getProblem().machines_neighborhoods[m_index];
					// System.out.printf(
					// "Process %d Machine %d neighborhood %d\n", p_index,
					// m_index, ni_index);
					neighborhoods.add(ni_index);
				}
				// System.out.println("Neighborhoods " + neighborhoods);
				for (Integer p_index : env.getProblem().processesListForServices[s_indexa]) {
					int m_index = potential_solution.processes[p_index];
					int n_i = env.getProblem().machines_neighborhoods[m_index];
					if (!neighborhoods.contains(new Integer(n_i))) {
						// System.out.printf("Neighborhoods %s (%d - %d) \n",
						// neighborhoods, m_index, n_i);
						assert (!Problem.VERBOSE): String.format(
								"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d of service %d should run in the neighborhood of a process of service %d runs.\n",
											p_index, s_indexa, service_dep);
						flag = false;
						break outer;// PA 19112011 Changed to accelerate
									// evaluation
					}
				}
			}
		}
		return flag;
	}

	@Deprecated
	public boolean isFeasibleForDependencyConstraints(int pId, int orig_mId,
			int mId) {

		boolean flag = true;

		int s_indexa = env.getProblem().getServiceIdForProcessId(pId);
		int old_n_i = env.getProblem().machines_neighborhoods[orig_mId];
		int new_n_i = env.getProblem().machines_neighborhoods[mId];

		if (old_n_i == new_n_i)
			return true;

		if (env.getProblem().service_dependencies[s_indexa].isEmpty()
				&& env.getProblem().servicesRequiredBy[s_indexa].isEmpty())
			return true;
		
		Solution potential_solution = env.getPotentialSolution();
		
		boolean thereIsAnotherProcessOfTheServiceInTheOldNeighborhood = false;
		if (!env.getProblem().servicesRequiredBy[s_indexa].isEmpty()) {
			boolean thereIsADependentProcessInTheOldNeighborhood = false;
			// Check if there is any dependent processes in the old neighborhood
			for (Integer s_index : env.getProblem().servicesRequiredBy[s_indexa]) {
				for (Integer p_index : env.getProblem().processesListForServices[s_index]) {
					int m_index = potential_solution.processes[p_index];
					int ni_index = env.getProblem().machines_neighborhoods[m_index];
					if (ni_index == old_n_i) {
						thereIsADependentProcessInTheOldNeighborhood = true;
						break;
					}
				}
			}

			if (thereIsADependentProcessInTheOldNeighborhood) {
				for (Integer p_index : env.getProblem().processesListForServices[s_indexa]) {
					if (p_index == pId)
						continue;
					int m_index = potential_solution.processes[p_index];
					int ni_index = env.getProblem().machines_neighborhoods[m_index];
					if (ni_index == old_n_i) {
						thereIsAnotherProcessOfTheServiceInTheOldNeighborhood = true;
						break;
					}
				}

				if (!thereIsAnotherProcessOfTheServiceInTheOldNeighborhood) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not out of neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
										pId, old_n_i, s_indexa);
					return false;
				}
			}
		}

		// At this point it is legal to remove the process

		// Check if there Is At Least On Process For Each Service It Depends In
		// The New Neighborhood

		for (Integer service_dep : env.getProblem().service_dependencies[s_indexa]) {
			boolean foundProcessInNewNeighborhood = false;
			for (Integer p_index : env.getProblem().processesListForServices[service_dep]) {
				int m_index = potential_solution.processes[p_index];
				int ni_index = env.getProblem().machines_neighborhoods[m_index];
				if (ni_index == new_n_i) {
					foundProcessInNewNeighborhood = true;
					break;
				}
			}
			if (!foundProcessInNewNeighborhood) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not move to neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
									pId, new_n_i, service_dep);
				return false;
			}
		}

		return true;
	}

	private boolean isFeasibleForDependencyConstraintsFast(int pId,
			int orig_mId, int mId) {
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();

		int s_indexa = problem.getServiceIdForProcessId(pId);
		int old_n_i = problem.machines_neighborhoods[orig_mId];
		int new_n_i = problem.machines_neighborhoods[mId];

		if (old_n_i == new_n_i)
			return true;

		if (problem.service_dependencies[s_indexa].isEmpty()
				&& problem.servicesRequiredBy[s_indexa].isEmpty())
			return true;

		if (pot_sol.serviceProcessesPerNeighborhood[s_indexa][old_n_i] == 0 && 
				!problem.servicesRequiredBy[s_indexa].isEmpty()) {
			// Check if there is any dependent processes in the old neighborhood
			for (Integer s_index : problem.servicesRequiredBy[s_indexa]) {
				if (pot_sol.serviceProcessesPerNeighborhood[s_index][old_n_i] > 0) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not out of neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
										pId, old_n_i, s_indexa);
					return false;
				}
			}
		}

		// At this point it is legal to remove the process

		// Check if there Is At Least On Process For Each Service It Depends In
		// The New Neighborhood if there is no other process of the same service

		if(pot_sol.serviceProcessesPerNeighborhood[s_indexa][new_n_i]<=1){
			for (Integer service_dep : problem.service_dependencies[s_indexa]) {
				if (!(pot_sol.serviceProcessesPerNeighborhood[service_dep][new_n_i] > 0)) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not move to neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
								pId, new_n_i, service_dep);
					return false;
				}
			}
		}

		return true;
	}
	
	private boolean isFeasibleForDependencyConstraintsFastNoSchedule(int pId,
			int orig_mId, int mId) {
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		int old_n_i = problem.machines_neighborhoods[orig_mId];
		int new_n_i = problem.machines_neighborhoods[mId];

		if (old_n_i == new_n_i)
			return true;
		
		int s_indexa = problem.getServiceIdForProcessId(pId);

		if (!problem.servicesRequiredBy[s_indexa].isEmpty() && 
				pot_sol.serviceProcessesPerNeighborhood[s_indexa][old_n_i] == 1) {
			// Check if there is any dependent processes in the old neighborhood
			for (Integer s_index : problem.servicesRequiredBy[s_indexa]) {
				if (pot_sol.serviceProcessesPerNeighborhood[s_index][old_n_i] > 0) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not out of neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
										pId, old_n_i, s_indexa);
					return false;
				}
			}
		}

		// At this point it is legal to remove the process

		// Check if there Is At Least On Process For Each Service It Depends In
		// The New Neighborhood if there is no other process of the same service

		if(!problem.service_dependencies[s_indexa].isEmpty() &&
				pot_sol.serviceProcessesPerNeighborhood[s_indexa][new_n_i]==0){
			for (Integer service_dep : problem.service_dependencies[s_indexa]) {
				if (pot_sol.serviceProcessesPerNeighborhood[service_dep][new_n_i] == 0) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not move to neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
								pId, new_n_i, service_dep);
					return false;
				}
			}
		}

		return true;
	}
	
	private boolean isFeasibleForDependencyConstraintsFastNoSchedule(int pId1,
			int mId1, int pId2, int mId2) {
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		int mId1_n = problem.machines_neighborhoods[mId1];
		int mId2_n = problem.machines_neighborhoods[mId2];

		//If we exchange processes in the same neighborhood no dependency
		//changes will be done and thus as we move in the legal space
		//the result will always be legal
		if (mId1_n == mId2_n)
			return true;
		
		int s1_index = problem.getServiceIdForProcessId(pId1);
		int s2_index = problem.getServiceIdForProcessId(pId2);
		
		//If we exchange processes of the same service no dependency
		//changes will be done and thus as we move in the legal space
		//the result will always be legal
		if (s1_index == s2_index)
			return true;

		
		if (!problem.servicesRequiredBy[s1_index].isEmpty() && 
				pot_sol.serviceProcessesPerNeighborhood[s1_index][mId1_n] == 1) {
			// Check if there is any dependent processes in the old neighborhood
			for (Integer s_index : problem.servicesRequiredBy[s1_index]) {
				if (pot_sol.serviceProcessesPerNeighborhood[s_index][mId1_n] > 0 || s_index==s2_index) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not out of neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
										pId1, mId1_n, s1_index);
					return false;
				}
			}
		}

		// At this point it is legal to remove the pId1 process

		// Check if there Is At Least On Process For Each Service It Depends In
		// The New Neighborhood if there is no other process of the same service

		if(!problem.service_dependencies[s1_index].isEmpty() &&
				pot_sol.serviceProcessesPerNeighborhood[s1_index][mId2_n]==0){
			for (Integer service_dep : problem.service_dependencies[s1_index]) {
				if (pot_sol.serviceProcessesPerNeighborhood[service_dep][mId2_n] == 0 ||
					( service_dep == s2_index && pot_sol.serviceProcessesPerNeighborhood[service_dep][mId2_n] == 1)) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not move to neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
								pId1, mId2_n, service_dep);
					return false;
				}
			}
		}
		
		// It is now legal to move the pId1 to mId2 
		
		if (!problem.servicesRequiredBy[s2_index].isEmpty() && 
				pot_sol.serviceProcessesPerNeighborhood[s2_index][mId2_n] == 1) {
				// Check if there is any dependent processes in the old neighborhood
				for (Integer s_index : problem.servicesRequiredBy[s2_index]) {
					if (pot_sol.serviceProcessesPerNeighborhood[s_index][mId2_n] > 0 || s_index==s1_index) {
						assert (!Problem.VERBOSE): String.format(
								"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not out of neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
											pId2, mId2_n, s2_index);
						return false;
					}
				}
			}

			// At this point it is legal to remove the  pId2 process

			// Check if there Is At Least On Process For Each Service It Depends In
			// The New Neighborhood if there is no other process of the same service

			if(!problem.service_dependencies[s2_index].isEmpty() &&
					pot_sol.serviceProcessesPerNeighborhood[s2_index][mId1_n]==0){
				for (Integer service_dep : problem.service_dependencies[s2_index]) {
					if (pot_sol.serviceProcessesPerNeighborhood[service_dep][mId1_n] == 0 ||
						( service_dep == s1_index && pot_sol.serviceProcessesPerNeighborhood[service_dep][mId1_n] == 1)) {
						assert (!Problem.VERBOSE): String.format(
								"Not feasible [DEPENDENCY CONSTRAINT VIOLATION] Process %d can not move to neighborhood %d. At least a process of service %d should run in the neighborhood.\n",
									pId2,mId1_n, service_dep);
						return false;
					}
				}
			}

		return true;
	}

	private boolean isFeasibleForSpreadConstraints() {

		for (int s_index = 0; s_index < env.getProblem().S; s_index++) {
			int spreadMin = env.getProblem().services.get(s_index).spreadMin;
			Set<Integer> aSet = new HashSet<Integer>();
			for (Integer p_index : env.getProblem().processesListForServices[s_index]) {
				int m_index = env.getPotentialSolution().processes[p_index];
				aSet.add(env.getProblem().machines_locations[m_index]);
			}
			if (aSet.size() < spreadMin) {
				assert (!Problem.VERBOSE): String.format(
								"Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
									s_index, spreadMin, aSet.size(), aSet);
				return false;
			}
		}
		return true;
	}

	// It could be faster
	@Deprecated
	public boolean isFeasibleForSpreadConstraints(int pId, int orig_mId, int mId) {
		int s_index = env.getProblem().getServiceIdForProcessId(pId);
		int spreadMin = env.getProblem().services.get(s_index).spreadMin;
		Set<Integer> aSet = new HashSet<Integer>();
		for (Integer p_index : env.getProblem().processesListForServices[s_index]) {
			int m_index = env.getPotentialSolution().processes[p_index];
			aSet.add(env.getProblem().machines_locations[m_index]);
		}
		if (aSet.size() < spreadMin) {
			assert (!Problem.VERBOSE): String.format("Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
								s_index, spreadMin, aSet.size(), aSet);
			return false;
		}
		return true;
	}
	
	private boolean isFeasibleForSpreadConstraintsFast(int pId, int orig_mId,
			int mId) {
		int s_index = env.getProblem().getServiceIdForProcessId(pId);
		int spreadMin = env.getProblem().services.get(s_index).spreadMin;
		if (env.getPotentialSolution().serviceProcessesPerLocation[s_index][env.getProblem().L] < spreadMin) {
			assert (!Problem.VERBOSE): String.format(
					"Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
								s_index,
								spreadMin,
								env.getPotentialSolution().serviceProcessesPerLocation[s_index][env.getProblem().L],
								env.getPotentialSolution().serviceProcessesPerLocation[s_index]);
			return false;
		}
		return true;
	}
	
	private boolean isFeasibleForSpreadConstraintsFastNoSchedule(int pId, int orig_mId,
			int mId) {	
		int orig_location = env.getProblem().machines_locations[orig_mId];
		int new_location = env.getProblem().machines_locations[mId];
		Solution pot_sol = env.getPotentialSolution();
		if(orig_location!=new_location){
			int s_index = env.getProblem().getServiceIdForProcessId(pId);
			int spreadMin = env.getProblem().services.get(s_index).spreadMin;

			int count_change=0;
			if (pot_sol.serviceProcessesPerLocation[s_index][orig_location] == 1)
				count_change--;
			if (pot_sol.serviceProcessesPerLocation[s_index][new_location] == 0)
				count_change++;

			if (pot_sol.serviceProcessesPerLocation[s_index][env.getProblem().L] + count_change < spreadMin) {
				assert (!Problem.VERBOSE): String.format(
							"Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
							s_index,
							spreadMin,
							pot_sol.serviceProcessesPerLocation[s_index][env.getProblem().L]+ count_change,
							pot_sol.serviceProcessesPerLocation[s_index]);
				return false;
			}
		}
		return true;
	}
	
	private boolean isFeasibleForSpreadConstraintsFastNoSchedule(int pId1, int mId1, 
			int pId2, int mId2) {
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		int s1_index = problem.getServiceIdForProcessId(pId1);
		int s2_index = problem.getServiceIdForProcessId(pId2);
		
		// If we exchange to processes of the same service the spread
		// will not be effected
		if(s1_index==s2_index)
			return true;

		int mId1_loc = problem.machines_locations[mId1];
		int mId2_loc = problem.machines_locations[mId2];
		// If we exchange processes of the same location the spread of their 
		// respected services will not be effected
		if(mId1_loc!=mId2_loc){
			int spread1Min = problem.services.get(s1_index).spreadMin;
			int count_change1=0;
			if (pot_sol.serviceProcessesPerLocation[s1_index][mId1_loc] == 1)
				count_change1--;
			if (pot_sol.serviceProcessesPerLocation[s1_index][mId2_loc] == 0)
				count_change1++;

			if (pot_sol.serviceProcessesPerLocation[s1_index][problem.L] + count_change1 < spread1Min) {
				assert (!Problem.VERBOSE): String.format(
							"Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
							s1_index,
							spread1Min,
							pot_sol.serviceProcessesPerLocation[s1_index][problem.L]+ count_change1,
							pot_sol.serviceProcessesPerLocation[s1_index]);
				return false;
			}
			
			int spread2Min = problem.services.get(s2_index).spreadMin;
			int count_change2=0;
			if (pot_sol.serviceProcessesPerLocation[s2_index][mId2_loc] == 1)
				count_change2--;
			if (pot_sol.serviceProcessesPerLocation[s2_index][mId1_loc] == 0)
				count_change2++;

			if (pot_sol.serviceProcessesPerLocation[s2_index][problem.L] + count_change2 < spread2Min) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [SPREAD CONSTRAINT VIOLATION] s%d should have spreadMin=%d but is %d %s\n",
							s2_index,
							spread1Min,
							pot_sol.serviceProcessesPerLocation[s2_index][problem.L]+ count_change2,
							pot_sol.serviceProcessesPerLocation[s2_index]);
				return false;
			}
		}
		return true;
	}

	public boolean isFeasibleForTransientConstraints() {
		boolean flag = true;
		outer: for (int m = 0; m < env.getProblem().M; m++)
			for (int r = 0; r < env.getProblem().RE; r++) {
				Resource resource = env.getProblem().getResourceByIndex(r);
				if (resource.isTransientUsage()) {
					long sum = 0;
					for (int p = 0; p < env.getProblem().P; p++) {
						int initial_state_machine = env.getInitialSolution().processes[p];
						int new_state_machine = env.getPotentialSolution().processes[p];
						if ((initial_state_machine == m)
								|| (new_state_machine == m)) {
							sum += env.getProblem().getR(p, r);
						}
					}
					if (sum > env.getProblem().getC(m, r)) {
						assert (!Problem.VERBOSE): String.format(
								"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
											m, r);
						flag = false;
						break outer;// PA 19112011 Changed to accelerate
									// evaluation
					}
				}
			}
		return flag;
	}

	@Deprecated
	private boolean isFeasibleForTransientConstraints(int pId, int orig_mId,
			int mId) {
		boolean flag = true;
		int r;
		for (int j = 0; j < env.getProblem().TRE.length; j++) {
			r = env.getProblem().TRE[j];
			long sum1 = 0, sum2 = 0;
			// long sum1f =
			// new_solution.usage[mId][r]+initial_solution.usage[mId][r];
			// long sum2f =
			// new_solution.usage[orig_mId][r]+initial_solution.usage[orig_mId][r];
			for (int p = 0; p < env.getProblem().P; p++) {
				int initial_state_machine = env.getInitialSolution().processes[p];
				int new_state_machine = env.getPotentialSolution().processes[p];
				if ((initial_state_machine == mId)
						|| (new_state_machine == mId)) {
					sum1 += env.getProblem().getR(p, r);
				}
				if ((initial_state_machine == orig_mId)
						|| (new_state_machine == orig_mId)) {
					sum2 += env.getProblem().getR(p, r);
				}
			}
			// assert(sum1==sum1f &&
			// sum2==sum2f):"sum1="+sum1+", sum1f="+sum1f+", sum2="+sum2+", sum2f="+sum2f;
			if (sum1 > env.getProblem().getC(mId, r)
					|| sum2 > env.getProblem().getC(orig_mId, r)) {
				assert (!Problem.VERBOSE): String.format(
								"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
									mId, r);
				flag = false;
				return flag;// PA 19112011 Changed to accelerate evaluation
			}
		}
		return flag;
	}

//	@Deprecated
//	private boolean isFeasibleForTransientConstraintsFast(int pId, int orig_mId,
//			int mId) {
//		Solution potential_solution = env.getPotentialSolution();
//		int[] procs = potential_solution.processes;
//		long[] sums = new long[env.getProblem().TRE.length];
//		List<Integer> orig_proc = env.getInitialSolution().processesListForMachines[mId];
//
//		// Calculate moved out process contribution
//		for (Integer p : orig_proc) {
//			// The process has moved
////			if (getCurrentSolution().machinesByProcesses[mId][p] == 0)
//			if (procs[p] != mId){
//				for (int j = 0; j < env.getProblem().TRE.length; j++) {
//					sums[j] += env.getProblem().getR(p, env.getProblem().TRE[j]);
//				}
//			}
//		}	
//		// Add the current usage			
//		for (int j = 0; j < env.getProblem().TRE.length; j++) {
//			sums[j] += potential_solution.usage[mId][env.getProblem().TRE[j]];
//			if (sums[j] > env.getProblem().getC(mId, env.getProblem().TRE[j])) {
//				assert (!Problem.VERBOSE): String.format(
//							"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
//									mId, env.getProblem().TRE[j]);
//				return false;
//			}
//		}
//		return true;
//	}

	private boolean isFeasibleForTransientConstraintsSuperFast(int pId, int orig_mId,
			int mId) {	
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		// Add the current usage			
		for (int j = 0; j < problem.TRE.length; j++) {
			long sum = pot_sol.usage[mId][env.getProblem().TRE[j]] +
					pot_sol.transientResourceUsageOfMachine[mId][j];
			if (sum > env.getProblem().getC(mId, env.getProblem().TRE[j])) {
				assert (!Problem.VERBOSE): String.format(
								"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
									mId, env.getProblem().TRE[j]);
				return false;
			}
		}
		return true;
	}
	
	private boolean isFeasibleForTransientConstraintsSuperFastNoSchedule(int pId, int orig_mId,
			int mId) {	
		int initial_machine = env.getInitialSolution().processes[pId];
		//If it is returning to the initial machine the process resource usage has
		//already been calculated in the transientResourceUsageOfMachine and 
		//because we are moving from legal to legal it is sufficient to return true;
		if(initial_machine==mId)
			return true;
		int r=0;
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		// Add the current usage			
		for (int j = 0; j < env.getProblem().TRE.length; j++) {
			r = problem.TRE[j];
			long sum = pot_sol.usage[mId][r] +
					pot_sol.transientResourceUsageOfMachine[mId][j] + problem.getR(pId, r);
			if (sum > problem.getC(mId, r)) {
				assert (!Problem.VERBOSE): String.format(
								"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
									mId, r);
				return false;
			}
		}
		return true;
	}
	
	public boolean isFeasibleForTransientConstraintsSuperFastNoSchedule(int pId1, int mId1, int pId2,
			int mId2) {	
		int initial_machine1 = env.getInitialSolution().processes[pId1];
		int initial_machine2 = env.getInitialSolution().processes[pId2];
		int r=0;
		Problem problem = env.getProblem();
		Solution pot_sol = env.getPotentialSolution();
		
		//If it is returning to the initial machine the process resource usage has
		//already been calculated in the transientResourceUsageOfMachine and 
		//because we are moving in legal space no resource violation can happen
		if(initial_machine1!=mId2){
			// Add the current usage			
			for (int j = 0; j < problem.TRE.length; j++) {
				r = problem.TRE[j];
				long sum = pot_sol.usage[mId2][r] +
						pot_sol.transientResourceUsageOfMachine[mId2][j] + problem.getR(pId1, r);
				// If pId2 process was not in mId2 machine in the initial solution
				// subtract r resource usage from usage. If it was in mId2 in the initial
				// solution r resource usage should be move to the transient resource usage
				// and thus no subtraction should be performed
				if(mId2!=initial_machine2)
					sum-=problem.getR(pId2, r);
				if (sum > problem.getC(mId2, r)) {
					assert (!Problem.VERBOSE): String.format(
						"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
								mId2, r);
					return false;
				}
			}
		}
		
		//If it is returning to the initial machine the process resource usage has
		//already been calculated in the transientResourceUsageOfMachine and 
		//because we are moving in legal space no resource violation can happen
		if(initial_machine2!=mId1){
			// Add the current usage			
			for (int j = 0; j < problem.TRE.length; j++) {
				r = problem.TRE[j];
				long sum = pot_sol.usage[mId1][r] +
						pot_sol.transientResourceUsageOfMachine[mId1][j] + problem.getR(pId2, r);
				// If pId1 process was not in mId1 machine in the initial solution
				// subtract r resource usage from usage. If it was in mId1 in the initial
				// solution r resource usage should be move to the transient resource usage
				// and thus no subtraction should be performed
				if(mId1!=initial_machine1)
					sum-=problem.getR(pId1, r);
				if (sum > problem.getC(mId1, r)) {
					assert (!Problem.VERBOSE): String.format(
							"Not feasible [TRANSIENT CONSTRAINT VIOLATION]. Machine %d and resource %d capacity problem\n",
								mId1, r);
					return false;
				}
			}
		}
		return true;
	}

	public boolean isFinallyFeasible(int p_index, int orig_m_index, int m_index) {
		// EtmPoint point = etmMonitor
		// .createPoint("IncrementalValidator:isFinallyFeasible");
		boolean flag = true;
		if (flag
				&& !isFeasibleForSpreadConstraintsFast(p_index, orig_m_index,
						m_index))
			flag = false;
		if (flag
				&& !isFeasibleForDependencyConstraintsFast(p_index,
						orig_m_index, m_index))
			flag = false;
		
		if (flag
				&& !isFeasibleForTransientConstraintsSuperFast(p_index,
						orig_m_index, m_index))
			flag = false;
		// point.collect();
		return flag;
	}
	
	public boolean isFinallyFeasibleNoSchedule(int p_index, int orig_m_index, int m_index) {
		if (isFeasibleForTransientConstraintsSuperFastNoSchedule(p_index, orig_m_index, m_index)&&
				isFeasibleForDependencyConstraintsFastNoSchedule(p_index, orig_m_index, m_index) &&
				isFeasibleForSpreadConstraintsFastNoSchedule(p_index, orig_m_index, m_index)			 
			)
			return true;
		else
			return false;
	}
	
	public boolean isMachineCapacityFeasible(int m_index) {
		for (int j = 0; j < env.getProblem().SRE.length; j++)
			if (env.getPotentialSolution().usage[m_index][env.getProblem().SRE[j]] > env.getProblem().C[m_index][env.getProblem().SRE[j]]) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [CAPACITY VIOLATION] Machine:%d resource:%d (needed:%d available:%d)\n",
									m_index,
									env.getProblem().SRE[j],
									env.getPotentialSolution().usage[m_index][env.getProblem().SRE[j]],
									env.getProblem().C[m_index][env.getProblem().SRE[j]]);
				return false;
			}
		return true;
	}

	public boolean isMachineCapacityFeasible(int p_index, int m_index) {
		for (int j = 0; j < env.getProblem().SRE.length; j++)
			if (env.getPotentialSolution().usage[m_index][env.getProblem().SRE[j]]
					+ env.getProblem().getR(p_index, env.getProblem().SRE[j]) > env.getProblem().C[m_index][env.getProblem().SRE[j]]) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [CAPACITY VIOLATION] Machine:%d resource:%d (needed:%d available:%d)\n",
									m_index,
									env.getProblem().SRE[j],
									env.getPotentialSolution().usage[m_index][env.getProblem().SRE[j]]
											+ env.getProblem().getR(p_index,
													env.getProblem().SRE[j]),
									env.getProblem().C[m_index][env.getProblem().SRE[j]]);
				return false;
			}
		return true;
	}
	
	public boolean isMachineCapacityFeasible(int p_un_index, int p_index, int m_index) {
		Problem problem = env.getProblem();
		int[] SRE = problem.SRE;
		Solution potential_solution = env.getPotentialSolution();
		for (int j = 0; j < SRE.length; j++){
			if (potential_solution.usage[m_index][SRE[j]]
					- problem.getR(p_un_index, SRE[j])
					+ problem.getR(p_index, SRE[j]) 
					> problem.C[m_index][SRE[j]]) {
				assert (!Problem.VERBOSE): String.format(
						"Not feasible [CAPACITY VIOLATION] Machine:%d resource:%d (needed:%d available:%d)\n",
									m_index,
									problem.SRE[j],
									potential_solution.usage[m_index][SRE[j]]
											- problem.getR(p_un_index, SRE[j])
											+ problem.getR(p_index, SRE[j]) 
											,
									env.getProblem().C[m_index][SRE[j]]);
				return false;
			}
		}
		return true;
	}

	// PA19112011
	@Deprecated
	public boolean isProbablyFeasible(int p_index, int orig_m_index, int m_index) {
		return isMachineCapacityFeasible(m_index)
				&& processHasNoConflictAtMachine(p_index, m_index)
				&& areProcessMachineMoveIndependent(orig_m_index, m_index);
	}

	public boolean isProbablyFeasibleNoSchedule(int p_index, int orig_m_index,
			int m_index) {
//		if(env.getIllegalMachines(p_index).contains(m_index))
//			return false;
//		else
		    return isMachineCapacityFeasible(p_index, m_index)
				&& processHasNoConflictAtMachineNotScheduled(p_index, m_index)
		// && areProcessMachineMoveIndependent(orig_m_index, m_index)
		;
	}

	public boolean isProcessExchangeFeasible(int p_id1, int p_id2) {
		Solution potential_solution = env.getPotentialSolution();
		
		int orig_m_id1 = potential_solution.getMachineIdForProcessId(p_id1);
		int orig_m_id2 = potential_solution.getMachineIdForProcessId(p_id2);

		boolean flag = true;

		potential_solution.removeProcess(p_id1);
		potential_solution.removeProcess(p_id2);
		potential_solution.assignProcessToMachine(p_id1, orig_m_id2);
		potential_solution.assignProcessToMachine(p_id2, orig_m_id1);
//		updateServiceProcessesDistribution(p_id1, orig_m_id1, orig_m_id2);
//		updateServiceProcessesDistribution(p_id2, orig_m_id2, orig_m_id1);
//		updateTransientResourceUsageOfMachine(p_id1, orig_m_id1, orig_m_id2);
//		updateTransientResourceUsageOfMachine(p_id2, orig_m_id2, orig_m_id1);

		flag = isMachineCapacityFeasible(orig_m_id2);
		if (flag && !processHasNoConflictAtMachine(p_id1, orig_m_id2))
			flag = false;
		if (flag && !isMachineCapacityFeasible(orig_m_id1))
			flag = false;
		if (flag && !processHasNoConflictAtMachine(p_id2, orig_m_id1))
			flag = false;
		if (flag
				&& !isFeasibleForSpreadConstraintsFast(p_id1, orig_m_id1,
						orig_m_id2))
			flag = false;
		if (flag
				&& !isFeasibleForDependencyConstraintsFast(p_id1, orig_m_id1,
						orig_m_id2))
			flag = false;
		if (flag
				&& !isFeasibleForSpreadConstraintsFast(p_id2, orig_m_id2,
						orig_m_id1))
			flag = false;
		if (flag
				&& !isFeasibleForDependencyConstraintsFast(p_id2, orig_m_id2,
						orig_m_id1))
			flag = false;
		
		if (flag
				&& !isFeasibleForTransientConstraintsSuperFast(p_id1, orig_m_id1,
						orig_m_id2))
			flag = false;
		if (flag
				&& !isFeasibleForTransientConstraintsSuperFast(p_id2, orig_m_id2,
						orig_m_id1))
			flag = false;

		potential_solution.removeProcess(p_id1);
		potential_solution.removeProcess(p_id2);
		potential_solution.assignProcessToMachine(p_id1, orig_m_id1);
		potential_solution.assignProcessToMachine(p_id2, orig_m_id2);
//		updateServiceProcessesDistribution(p_id1, orig_m_id2, orig_m_id1);
//		updateServiceProcessesDistribution(p_id2, orig_m_id1, orig_m_id2);
//		updateTransientResourceUsageOfMachine(p_id1, orig_m_id2, orig_m_id1);
//		updateTransientResourceUsageOfMachine(p_id2, orig_m_id1, orig_m_id2);

		return flag;
	}
	
	public boolean isProcessExchangeFeasibleNoSchedule(int p_id1, int p_id2) {
		Solution potential_solution = env.getPotentialSolution();
		int orig_m_id1 = potential_solution.getMachineIdForProcessId(p_id1);
		int orig_m_id2 = potential_solution.getMachineIdForProcessId(p_id2);
		return isProcessExchangeFeasibleNoSchedule(p_id1, orig_m_id1, p_id2, orig_m_id2);
	}
	
	public boolean isProcessExchangeFeasibleNoSchedule(int p_id1, int orig_m_id1, int p_id2, int orig_m_id2) {
//		if(env.getIllegalMachines(p_id1).contains(orig_m_id2) || 
//				env.getIllegalMachines(p_id2).contains(orig_m_id1))
//			return false;
		if (!processHasNoConflictAtMachine(p_id2, p_id1, orig_m_id2))
			return false;
		if (!processHasNoConflictAtMachine(p_id1, p_id2, orig_m_id1))
			return false;
		if(!isMachineCapacityFeasible(p_id2, p_id1, orig_m_id2))
			return false;
		if (!isMachineCapacityFeasible(p_id1, p_id2, orig_m_id1))
			return false;
		if (!isFeasibleForTransientConstraintsSuperFastNoSchedule(p_id1, orig_m_id1, p_id2,
				orig_m_id2))
			return false;
		if (!isFeasibleForDependencyConstraintsFastNoSchedule(p_id1, orig_m_id1, p_id2,
				orig_m_id2))
			return false;
		if (!isFeasibleForSpreadConstraintsFastNoSchedule(p_id1, orig_m_id1, p_id2,
						orig_m_id2))
			return false;

		return true;
	}

	public boolean processHasNoConflictAtMachine(int p_index, int m_index) {
		// conflict constraint
		int s_id = env.getProblem().getServiceIdForProcessId(p_index);
		Solution potential_solution = env.getPotentialSolution();
		for (Integer process_index : env.getProblem().processesListForServices[s_id]) {
			if (process_index == p_index)
				continue;
			if (potential_solution.processes[process_index] == m_index)
				return false;
		}
		return true;
	}
	
	public boolean processHasNoConflictAtMachine(int p_un_index, int p_index, int m_index) {
		// conflict constraint
		int s_id = env.getProblem().getServiceIdForProcessId(p_index);
		
		//If the process to be exchanged is from the same service and because
		//we move through legal solution space, no other process of the same
		//service should exist in this machine.
		int s_un_id = env.getProblem().getServiceIdForProcessId(p_un_index);
		if(s_id==s_un_id)
			return true;
		
		Solution potential_solution = env.getPotentialSolution();
		for (Integer process_index : env.getProblem().processesListForServices[s_id]) {
			if (potential_solution.processes[process_index] == m_index)
				return false;
		}
		return true;
	}

	public boolean processHasNoConflictAtMachineNotScheduled(int p_index,
			int m_index) {
		// conflict constraint
		int s_id = env.getProblem().getServiceIdForProcessId(p_index);
		Solution potential_solution = env.getPotentialSolution();
		for (Integer process_index : env.getProblem().processesListForServices[s_id]) {
			if (potential_solution.processes[process_index] == m_index)
				return false;
		}
		return true;
	}

	public void setEnv(SolverEnvironment env) {
		this.env = env;
		init();
	}

//	public void updateServiceProcessesDistribution(int pId, int orig_mId,
//			int mId) {
//		int s_index = env.getProblem().getServiceIdForProcessId(pId);
//
//		int orig_location = env.getProblem().machines_locations[orig_mId];
//		int new_location = env.getProblem().machines_locations[mId];
//		if (orig_location != new_location) {
//			int count_change = 0;
//
//			if (serviceProcessesPerLocation[s_index][orig_location] == 1)
//				count_change--;
//			serviceProcessesPerLocation[s_index][orig_location]--;
//
//			if (serviceProcessesPerLocation[s_index][new_location] == 0)
//				count_change++;
//			serviceProcessesPerLocation[s_index][new_location]++;
//
//			serviceProcessesPerLocation[s_index][env.getProblem().L] += count_change;
//		}
//
//		int orig_neighborhood = env.getProblem().machines_neighborhoods[orig_mId];
//		int new_neighborhood = env.getProblem().machines_neighborhoods[mId];
//		if (orig_neighborhood != new_neighborhood) {
//			int count_change = 0;
//
//			if (serviceProcessesPerNeighborhood[s_index][orig_neighborhood] == 1)
//				count_change--;
//			serviceProcessesPerNeighborhood[s_index][orig_neighborhood]--;
//
//			if (serviceProcessesPerNeighborhood[s_index][new_neighborhood] == 0)
//				count_change++;
//			serviceProcessesPerNeighborhood[s_index][new_neighborhood]++;
//
//			serviceProcessesPerNeighborhood[s_index][env.getProblem().N] += count_change;
//		}
//	}

	public void updateStructuresOnSolutionChange() {
//		createServiceProcessesDistribution();
//		createTransientResourceUsageOfMachine();
	}

//	public void updateTransientResourceUsageOfMachine(int pId, int orig_mId,
//			int mId) {
//		
//		Solution current = env.getCurrentSolution();
//		Solution initial = env.getInitialSolution();
//		Problem problem =  env.getProblem();
//		int[] init_procs = initial.processes;
//
//		if (orig_mId != init_procs[pId]){
//			if(mId == init_procs[pId]){
//				for (int j = 0; j < env.getProblem().TRE.length; j++) {
//					transientResourceUsageOfMachine[init_procs[pId]][j] -= problem.getR(pId, problem.TRE[j]);
//				}
//			}
//		}
//		else{
//			if(mId != init_procs[pId]){
//				for (int j = 0; j < env.getProblem().TRE.length; j++) {
//					transientResourceUsageOfMachine[init_procs[pId]][j] += problem.getR(pId, problem.TRE[j]);
//				}
//			}
//		}	
//	}

}
